import pao = require('../public');

/**
* 错误代码：文件找不到,Not Found
*/
const errorCode_FileNotFound = 404;

/**
 * 模块名称
 */
const moduleName = "pao.client";

/**
* 请求类型
*/
type reuqestType = "GET" | "POST";

/**
 * IE 浏览器兼容
 */
if (!String.prototype.startsWith) {
    String.prototype.startsWith = function (searchString, position) {
        return this.substr(position || 0, searchString.length) === searchString;
    };
}

//******************远程******************//
/**
    * 创建JsonAjax设置
    * @param url url
    * @param serviceName 服务名称
    * @param functionName 方法名称
    * @param args 参数
    */
function createJsonAjaxSettings(url: string, serviceName: string, functionName: string, args: any[]): JQueryAjaxSettings {
    let settings: JQueryAjaxSettings = {};
    let header: pao.remoteHeader;
    if (pao.currentUser) {
        header = {
            userID: pao.currentUser.name,
            password: pao.currentUser.password
        }
    }
    settings.url = url;
    settings.type = "POST";
    settings.dataType = "json";
    /** 发送信息前设置类型信息 */
    settings.data = pao.addonSerialize({
        serviceName: serviceName,
        functionName: functionName,
        header: header,
        args: args
    });
    return settings;
}

/**
    * 同步json调用
    * @param url 服务地址
    * @param serviceName 服务名称
    * @param functionName 方法名称
    * @param args 参数
    */
export function jsonCall(url: string, serviceName: string, functionName: string, args?: any[]): any {
    let settings: JQueryAjaxSettings = createJsonAjaxSettings(url, serviceName, functionName, args);
    let returnValue: any;
    let errMessage: any;
    settings.async = false;
    settings.crossDomain = true;
    settings.success = (response) => {
        returnValue = response;
        if (returnValue) {
            // 返回值处理前要获取类型原型
            pao.fetchAddonProtoType(returnValue);
        }
    };
    settings.error = (xhr, status, err) => {
        errMessage = pao.addonSerialize(xhr);
    };
    $.ajax(settings);
    if (returnValue)
        return returnValue.d;
    if (errMessage) {
        throw new Error(errMessage);
    }
    return undefined;
}

/**
    * 异步json调用
    * @param url 服务地址
    * @param serviceName 服务名称
    * @param functionName 方法名称
    * @param args 参数
    */
export function asyncJsonCall(url: string
    , serviceName: string
    , functionName: string
    , args?: any[]
    , success?: (result: any) => void
    , error?: (err: Error) => void) {
    let settings: JQueryAjaxSettings = createJsonAjaxSettings(url, serviceName, functionName, args);
    settings.async = true;
    settings.crossDomain = true;
    settings.success = (response) => {
        let returnValue = response;
        if (returnValue) {
            // 返回值处理前要获取类型原型
            pao.fetchAddonProtoType(returnValue);
        }
        if (success) {
            if (returnValue) {
                // 处理返回值
                success(returnValue.d);
            } else {
                success(undefined);
            }
        };
        
    };
    settings.error = (xhr, status, err) => {
        if (error) {
            error(new Error(JSON.stringify(xhr)));
        }
    };
    $.ajax(settings);
}

/**
* 脚本库列表
*/
let scriptLibs: string[] = [];

/**
* 动态加载JavaScript 库
* @param url 待加载库的url
* @param forceToDuplicateLoad 强制重新加载
* @remark url必须是../开头的相对地址
*/
export function loadScript(url: string, forceToDuplicateLoad: boolean = false) {
    /** 非强行加载 */
    if (!forceToDuplicateLoad && scriptLibs[url]) {
        return;
    }

    $.ajax({
        url: url,
        dataType: "script",
        async: false,
        success: (scr, status, xhr) => {
            // Execut JavaScript
            eval(scr);
            scriptLibs.push(url);
        },
        error: (xhr, status, err) => {
            // The JavaScript File not Found
            if (xhr.status == errorCode_FileNotFound)
                return;
            // Other Error Status
            else
                throw new Error(`${url}脚本加载异常, 状态码: ${xhr.status} , 错误描述 : ${err}`);
        }
    });
}

/**
    * 动态加载JavaScript库集
    * @param scripts
    */
export function loadScripts(scripts: string[]) {
    for (var script of scripts) {
        loadScript(script);
    }
}

/**
    * 动态加载 css 文件
    * @param url 待加载css文件的url
    * @param type 
    * @param rel
    */
export function loadStyle(url: string) {
    loadLink(url, "text/css", "stylesheet");
}

/**
    * 动态加载 css 文件集
    * @param urls css文件集url
    */
export function loadStyles(urls: string[]) {
    for (var url of urls) {
        loadStyle(url);
    }
}

/**
    * 动态加载Json
    * @param url 待加载Json的url
    * @remark url必须是../开头的相对地址
    */
export function loadJson(url: string): any {
    let jsonObject: any;

    $.ajax({
        url: url,
        dataType: "json",
        async: false,
        success: (scr, status, xhr) => {
            jsonObject = scr;
        },
        error: (xhr, status, err) => {
            throw new Error(`${url}加载异常, 状态码: ${xhr.status} , 错误描述 : ${err}`);
        }
    });

    return jsonObject;
}
/**
* 动态加载文本
* @param url 待加载文本的url
* @remark url必须是../开头的相对地址
*/
export function loadText(url: string): any {
    let text: string;

    $.ajax({
        url: url,
        dataType: "text",
        async: false,
        success: (scr, status, xhr) => {
            text = scr;
        },
        error: (xhr, status, err) => {
            throw new Error(`${url}加载异常, 状态码: ${xhr.status} , 错误描述 : ${err}`);
        }
    });

    return text;
}

/**
    * 动态加载 link 标签
    * @param url 待加载文件的url
    * @param type 文件类型
    * @param rel 
    */
export function loadLink(url: string, type: string, rel: string) {
    var link = document.createElement("link");
    link.type = type;
    link.rel = rel;
    link.href = url;
    document.getElementsByTagName("head")[0].appendChild(link);
}

/**
 * html
 * @param html 元素
 */
export function loadElement(html: string): Element {
    let element = document.createElement("div");
    element.innerHTML = html;
    return $(element).children[0];
}

/**
 * 远程工厂: 通过JSON实现远程调用
 */
@pao.addon("远程工厂",
    "通过JSON实现远程调用",
    moduleName)
export class remoteFactory extends pao.proxyFactory {
    constructor(public objectType: new () => any, public url: string, public serviceName: string) {
        super(objectType);
    }

    proxyMethod?(methodName: any, args: any): any {
        return jsonCall(this.url, this.serviceName, methodName, args);
    }
}

//******************应用******************//

/**
 * 主应用程序
 */
export let mainApplication: clientApplication;

/**
 * 客户端应用
 */
@pao.addon("客户端应用设置", "包含各种资源的客户端应用实体对象", moduleName)
export class clientApplication extends pao.application {
    /**
     * 主窗体
     */
    mainForm?: baseMainForm;

    start(): void {
        super.start();
        mainApplication = this;

        if (this.mainForm) {
            this.mainForm.render("#main_form_container");
        }
    }
}

/**
 * 启动客户端
 * @param clientApp 客户端应用
 * @param onLoad 加载事件
 * @param onError 错误事件
 */
export function startClient(clientApp: clientApplication, onLoad?: () => void, onError?: () => void) {
    window.onload = () => {
        pao.log("页面重新加载");
        clientApp.start();

        // 窗体加载后的动作
        if (onLoad)
            onLoad();
    };

    window.onerror = (err) => {
        if (onError) {
            onError();
        } else {
            // 未知异常记录到服务器上
            pao.logToServer(err, "error");
        }
    }
}

/**
 * 将html设置到容器中
 * @param html 控件html
 * @param css 控件风格
 */
export function setTo(container: string | Element | JQuery, html?: string, css?: string): baseControl {
    let control = new baseControl(html, css);
    control.setTo(container);
    return control;
}

/**
 * 将html添加到容器中 
 * @param html 控件html
 * @param css 控件风格
 */
export function appendTo(container: string | Element | JQuery, html?: string, css?: string): baseControl {
    let control = new baseControl(html, css);
    control.appendTo(container);
    return control;
}

//******************控件******************//
/**
 * 模板装饰器
 * @param html 模板内容
 * @param css 样式
 */
export function template(html?: string, css?: string) {
    return function (target: any) {
        target.prototype.html = html;
        target.prototype.css = css;
    };
}

/**
 * 控件基类
 */
@pao.addon("控件基类", "可以作为子对象插入到容器中的控件对象", moduleName)
export class baseControl extends pao.baseObject {
    /**
     * 视图模板
     */
    html?: string;
    /**
     * 视图样式文件
     */
    css?: string;


    constructor(html?: string, css?: string) {
        super();
        if (html)
            this.html = html;
        if (css)
            this.css = css;
    }

    /**
     * 元素
     */
    element?: Element;
    /**
     * 绑定前事件
     */
    onBeforeBind?() {
    };
    /**
     * 绑定后事件
     */
    onAfterBind?() {
    };
    /**
     * 事件：渲染前
     */
    onBeforeRender?() {
    };
    /**
     * 事件：渲染后
     */
    onAfterRender?() {
    };
    /**
     * 事件:显示后
     */
    onShowed?(){
    }

    /**
     * 渲染到容器
     * @param container 容器控件 
     */
    render?(container?: string | Element | JQuery, append: boolean = false): void {
        if (this.onBeforeRender) {
            this.onBeforeRender();
        }

        // 将html插入容器
        if (this.html) {
            let html = this.html;
            // 用<开头的是模板
            let stratChar: string = html[0];
            if (!html.startsWith("<")) {
                // 加载文件
                try {
                    html = loadText(html);
                } catch (err) {
                    // 如果加载html文件错误，则仍让当作普通html渲染
                    pao.log(err);
                }
            }

            let div = document.createElement("div");
            div.innerHTML = html;

            if (div.children.length > 1) {
                this.element = div;
            } else {
                this.element = div.children[0];
            }

            if (!append) {
                $(container).empty();
            }
            $(container).append(this.element);
        }
        // 加载css
        if (this.css) {
            loadStyle(this.css);
        }
        this.rebind();
        if (this.onAfterRender) {
            this.onAfterRender();
        }
    }

    /**
     * 绑定到元素
     * @param element 元素
     */
    bindTo?(element: Element) {
        if (this.onBeforeRender) {
            this.onBeforeRender();
        }

        this.element = element;

        this.rebind();
        if (this.onAfterRender) {
            this.onAfterRender();
        }
    }

    /**
     * 查找子控件
     * @param elementName 元素
     */
    findByName?(elementName: string): JQuery {
        return this.find(`[name='${elementName}']`);
    }

    find?(elementName: string): JQuery {
        return $(this.element).find(elementName);
    }

    /**
     * 添加到容器中
     * @param container 容器
     */
    appendTo?(container: string | Element | JQuery) {
        this.render(container, true);
    }

    /**
     * 重新绑定
     */
    rebind?() {
        if (this.onBeforeBind) {
            this.onBeforeBind();
        }

        if (this.element) {
            for (let propertyName in this) {
                if (propertyName.startsWith("$")) {
                    let controlName = propertyName.substr(1);
                    this[propertyName] = this.findByName(controlName);
                }
            }
        }

        // 绑定控件
        if (this.onAfterBind) {
            this.onAfterBind();
        }
    }

    /**
     * 增加风格类型
     * @param className 风格类型
     */
    addClass?(className: string) {
        $(this.element).addClass(className);
    }

    /**
     * 特性获取/设定
     * @param name 特性名称
     * @param value 特性值，如果不输入，则表示返回
     */
    attr?(name: string, value?: any): any {
        return $(this.element).attr(name, value);
    }

    /**
     * 风格获取/设定
     * @param name 风格名称
     * @param value 风格值，如果不输入，则表示返回
     */
    style?(name?: any, value?: any): any {
        return $(this.element).css(name, value);
    }
}

@pao.addon("菜单", "菜单类", moduleName)
export class menu extends pao.baseObject {
    /**
     * 菜单标题
     */
    caption?: string;
    /**
     * 子菜单
     */
    childMenus?: menu[];
    /**
     * 点击事件
     */
    onClick?: () => void;
}

/**
 * 视图基类
 */
@pao.addon("视图基类", "所有视图的基类，视图中包含一个菜单", moduleName)
export class baseView extends baseControl {
    /**
     * 标题
     */
    caption?: string;
    /**
     * 菜单
     */
    menus?: menu[];
}

/**
 * 主窗体基类
 */
@pao.addon("主窗体基类", "所有主窗体的基类", moduleName)
export class baseMainForm extends baseControl {
    /**
     * 导航到视图，如果视图是string类型，表示url，如果是baseView，表示直接导航到对象
     */
    navigate?(view: string | baseView, caption?: string): void {

    }

    getView?(view: string | baseView, caption?: string): baseView {
        let targetView: baseView;
        if (typeof view == "string") {
            if (view.endsWith(".html")) {
                // 加载html
                targetView = new baseView();
                targetView.html = view;
                if (caption) {
                    targetView.caption = caption;
                }
            } else {
                // 加载对象
                targetView = syncRequire(view).defaultObject;
            }
        } else {
            targetView = view;
        }
        return targetView;
    }
}
